package com.ubxtech.web.handler.auth.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.ubxtech.core.context.ContextConstants;
import com.ubxtech.core.context.ContextToken;
import com.ubxtech.core.context.ContextUtil;
import com.ubxtech.core.utils.SpringUtils;
import com.ubxtech.web.handler.auth.AuthCollectService;
import com.ubxtech.web.handler.auth.LoginAppSecretService;
import com.ubxtech.web.handler.auth.LoginCustomAuthService;
import com.ubxtech.core.dto.LoginAppSecretConfig;
import com.ubxtech.core.utils.SignCheckUtil;
import com.ubxtech.web.utils.WebUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

/**
 * 各种鉴权集合
 * @author Victor.Xiao
 * @since 2023-09-07 16:29
 **/
@Service
@Slf4j
public class AuthCollectServiceImpl implements AuthCollectService {


    /** 不一定初始化,不能注入 */
    private LoginAppSecretService loginAppSecretService;
    /** 不一定初始化,不能注入 */
    private LoginCustomAuthService loginCustomAuthService;

    /**
     * 微服务方式鉴权
     * @author Victor.Xiao
     * @since 2023-09-07 16:48
     * @param request request
     * @param response response
     * @param handler handler
     * @return boolean
     */
    @Override
    public boolean cloudAuth(HttpServletRequest request, HttpServletResponse response, Object handler){
        String userId = WebUtil.getHeader(request, ContextConstants.USER_ID);
        String token = WebUtil.getHeader(request, ContextConstants.TOKEN);
        String origin = WebUtil.getHeader(request, ContextConstants.HEADER_ORIGIN);
        boolean tokenFlag = StrUtil.isBlank(userId) || StrUtil.isBlank(token);
        boolean originFlag = StrUtil.isBlank(origin);
        //如果传了token或者origin即可认为是有权限,其它强鉴权可以用express(local && cloud)方式,再用local本地工程再详细鉴权
        //目前判断origin判断实际就已经放开了微服务之间的权限校验,因为网关和feign请求头都加了origin,值为spring.application.name
        if (originFlag && tokenFlag) {
            return false;
        }
        String username = WebUtil.getHeader(request, ContextConstants.USERNAME);
        String nickname = WebUtil.getHeader(request, ContextConstants.NICKNAME);
        String tenant = WebUtil.getHeader(request, ContextConstants.KEY_TENANT);
        String tenantMultiStr = WebUtil.getHeader(request, ContextConstants.KEY_TENANT_MULTI);
        String permission = WebUtil.getHeader(request, ContextConstants.HEADER_PERMISSION);
        String ignoreTenant = WebUtil.getHeader(request, ContextConstants.HEADER_IGNORE_TENANT);
        String areaIdsStr = WebUtil.getHeader(request, ContextConstants.HEADER_AREA_LIST);
        String adminFlagStr = WebUtil.getHeader(request, ContextConstants.HEADER_ADMIN_FLAG);
        ContextUtil.set(ContextConstants.USER_ID,userId);
        ContextUtil.set(ContextConstants.USERNAME,username);
        ContextUtil.set(ContextConstants.NICKNAME,nickname);
        ContextUtil.setTenant(tenant);

        ContextToken contextToken = new ContextToken();
        contextToken.setNickname(nickname);
        contextToken.setTenant(tenant);
        contextToken.setUserId(userId);
        contextToken.setUsername(username);
        contextToken.setToken(token);
        if (StrUtil.isNotBlank(adminFlagStr)) {
            contextToken.setAdminFlag(adminFlagStr);
        }
        if (StrUtil.isNotBlank(areaIdsStr)) {
            Set<String> stringSet = new HashSet<>();
            CollectionUtil.addAll(stringSet,areaIdsStr.split(","));
            contextToken.setAreaList(stringSet);
        }
        if (StrUtil.isNotBlank(tenantMultiStr)) {
            Set<String> tenantMulti = new HashSet<>();
            CollectionUtil.addAll(tenantMulti,tenantMultiStr.split(","));
            contextToken.setTenantMulti(tenantMulti);
        }
        if (StrUtil.isNotBlank(ignoreTenant) && "1".equalsIgnoreCase(ignoreTenant)) {
            //忽略租户
            ContextUtil.setIgnoreTenantFlag(true);
        }
        if (StrUtil.isNotBlank(permission)) {
            Set<String> permissionSet = new HashSet<>();
            CollectionUtil.addAll(permissionSet,permission.split(","));
            contextToken.setPermissions(permissionSet);
        }
        ContextUtil.setContextToken(contextToken);

        return true;
    }


    /**
     * 鉴权
     * @author Victor.Xiao
     * @since 2022-01-17 11:36
     * @param request request
     * @param response response
     * @param handler handler
     * @return boolean
     */
    @Override
    public boolean signAuth(HttpServletRequest request, HttpServletResponse response, Object handler){
        if (loginAppSecretService == null) {
            loginAppSecretService = SpringUtils.getBean(LoginAppSecretService.class);
        }
        String url = request.getRequestURL().toString();

        String appId = WebUtil.getHeader(request, ContextConstants.HEADER_APP_ID);
        String deviceId = WebUtil.getHeader(request, ContextConstants.HEADER_DEVICE_ID);
        String sign = WebUtil.getHeader(request, ContextConstants.HEADER_SIGN);
        boolean signFlag = checkSign(appId, deviceId, sign);
        if (!signFlag) {
            log.error("签名校验失败,api请求地址:[{}];appId:[{}];deviceId:[{}];sign:[{}];", url, appId, deviceId,sign);
            return false;
        }

        ContextUtil.set(ContextConstants.HEADER_APP_ID, appId);
        ContextUtil.set(ContextConstants.HEADER_DEVICE_ID, deviceId);
        ContextUtil.set(ContextConstants.HEADER_SIGN, sign);
        return true;
    }


    /**
     * 校验签名
     * @author Victor.Xiao
     * @since 2021-12-15 20:01
     * @param appId appId
     * @param deviceId deviceId
     * @param sign sign
     * @return boolean
     */
    private boolean checkSign(String appId, String deviceId,  String sign){
        //是否全局校验
        if (loginAppSecretService.getCheckFlag() == null) {
            log.error("签名校验CheckFlag未初始化");
            return false;
        }
        if (loginAppSecretService.getCheckFlag() == 0) {
            return true;
        }
        //参数校验,如果没有则都是失败
        if (StrUtil.isBlank(appId) || loginAppSecretService.getAppSecretsConfig() == null
                || loginAppSecretService.getAppSecretsConfig().size() == 0
                || loginAppSecretService.getAppSecretsConfig().get(appId) == null) {
            log.info("校验算法参数失败,{},{},{}",deviceId,appId,sign);
            return false;
        }
        LoginAppSecretConfig appConfig = loginAppSecretService.getAppSecretsConfig().get(appId);
        //不校验特定appId
        if (appConfig.getIgnoreFlag() != null && appConfig.getIgnoreFlag() == 1) {
            return true;
        }
        String secretKey = appConfig.getSecretKey();
        //需要校验,则需要参数校验,如果没有则都是失败
        if (StrUtil.isBlank(sign)|| StrUtil.isBlank(secretKey)) {
            log.info("校验算法secretKey参数失败,{},{},{},{}",deviceId,appId,sign,secretKey);
            return false;
        }
        //默认忽略时间校验
        int ignoreTimeFlag = Optional.ofNullable(appConfig.getIgnoreTimeFlag()).orElse(0);
        return SignCheckUtil.checkSign(deviceId, secretKey, sign,ignoreTimeFlag);
    }


    /**
     * 本地方式鉴权
     * @author Victor.Xiao
     * @since 2023-09-07 16:48
     * @param request request
     * @param response response
     * @param handler handler
     * @return boolean
     */
    @Override
    public boolean localAuth(HttpServletRequest request, HttpServletResponse response, Object handler){
        if (loginCustomAuthService == null) {
            loginCustomAuthService = SpringUtils.getBean(LoginCustomAuthService.class);
        }
        return loginCustomAuthService.auth(request,response,handler);
    }

}
